﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using BLToolkit.Data;

namespace Devsoft.DB.Models
{
    public class SmartTypeDBRouter:DBRouter
    {
        public SmartTypeDBRouter()
        {
            ReadWriteTimeOut = 1000 * 100;
            _path = new Dictionary<string, string>();
            _cache = new Dictionary<Type, string>();
        }

        public override string GetConnectionName(Type type)
        {
            if (null == type) return ConnectionStringName;
            lock(_cacheLock)
            {
                if (!_cache.ContainsKey(type))
                {
                    _pathLock.AcquireReaderLock(ReadWriteTimeOut);
                    try
                    {
                        #region Find the best connection matching with the given type

                        var nearestLevel = Int32.MaxValue;
                        string connectionString = ConnectionStringName;
                        string typeName = type.FullName;
                        var level = typeName.Split('.').Length;
                        foreach (var pathAndConn in _path)
                        {
                            if (typeName == pathAndConn.Key)
                            {
                                connectionString = pathAndConn.Value;
                                break;
                            }
                            if (typeName.StartsWith(pathAndConn.Key))
                            {
                                var targetLevel = pathAndConn.Key.Split('.').Length;
                                var k = level - targetLevel;
                                if (k < nearestLevel)
                                {
                                    nearestLevel = k;
                                    connectionString = pathAndConn.Value;
                                }
                            }
                        }

                        #endregion

                        _cache[type] = connectionString;
                    }
                    finally
                    {
                        _pathLock.ReleaseReaderLock();
                    }
                }
                return _cache[type];
            }
        }

        public virtual void Register<T>(string connectionName)
        {
            Register(connectionName, typeof(T));
        }

        public virtual void Register(string connectionName, string nameSpace)
        {
            _pathLock.AcquireWriterLock(ReadWriteTimeOut);
            try
            {
                _path.Add(nameSpace,connectionName);
            }
            finally
            {
                _pathLock.ReleaseWriterLock();
            }
        }
        public virtual void Register(string connectionName, params Type[] types)
        {
            _pathLock.AcquireWriterLock(ReadWriteTimeOut);
            try
            {
                foreach (var type in types)
                {
                    _path.Add(type.FullName,connectionName);
                }
            }
            finally
            {
                _pathLock.ReleaseWriterLock();
            }
        }

        public override void DisposeCurrentDB()
        {
            var connectionNames = _cache.Values.Distinct().ToArray();
            if (HttpContext.Current != null)
            {
                foreach (var connectionName in connectionNames)
                {
                    var key = "__WRSCDC_" + connectionName + "_" + HttpContext.Current.GetHashCode().ToString("x") + Thread.CurrentContext.ContextID.ToString();
                    var context = HttpContext.Current.Items[key];
                    if (context != null && context is DbManager)
                    {
                        (context as DbManager).Dispose();
                    }
                }
            }
            else
            {
                foreach (var connectionName in connectionNames)
                {
                    var key = string.Format("__WRSCDC_{1}_{0}", Thread.CurrentThread.ManagedThreadId.ToString(), connectionName);
                    LocalDataStoreSlot threadData = Thread.GetNamedDataSlot(key);
                    if (threadData != null)
                    {
                        var context = Thread.GetData(threadData);
                        if (context != null && context is DbManager)
                        {
                            (context as DbManager).Dispose();
                        }
                    }
                }
            }
        }

        public int ReadWriteTimeOut { get; set; }
        private readonly System.Threading.ReaderWriterLock _pathLock = new ReaderWriterLock();
        private Dictionary<string, string> _path;

        private Dictionary<Type, string> _cache;
        private readonly object _cacheLock = new object();


    }
}
